package tools

import (
	"errors"
	"fmt"
	"sort"
	"time"

	commonConstant "algo-sdk-go/common/constant"
)

type TimeInterval struct {
	Year   int `json:"year"`
	Month  int `json:"month"`
	Week   int `json:"week"`
	Day    int `json:"day"`
	Hour   int `json:"hour"`
	Min    int `json:"min"`
	Second int `json:"second"`
}

type TimeFormatParams struct {
	TimeStr           string
	TimeFormat        time.Time
	TimeAdd           *TimeInterval
	Location          *time.Location
	DecodeStyle       string
	FormatStyle       string
	SupportBeforeDawn bool // 支持归溯到凌晨
}

type TimeParams struct {
	StartTime       string
	EndTime         string
	StartTimeFormat time.Time
	EndTimeFormat   time.Time
	TimeInterval    TimeInterval
	Ops             CheckOps
}

type CheckOps struct {
	NotAllowStartTimeBeforeNow bool
	NotAllowEndTimeBeforeNow   bool
	NotAllowIntervalCheck      bool
}

func TimeNow() time.Time {
	location, err := time.LoadLocation(commonConstant.ZoneTimeSH)
	if err != nil {
		//logger.LogErrorf(commonConstant.LoadTimeError, ",err: %+v", err)
		return time.Now().Local()
	}
	return time.Now().In(location)
}
func TimeNowStr() string {
	location, err := time.LoadLocation(commonConstant.ZoneTimeSH)
	if err != nil {
		//logger.LogErrorf(commonConstant.LoadTimeError, ", error:%+v ", err)
		return time.Now().Local().Format(commonConstant.Layout)
	}
	return time.Now().In(location).Format(commonConstant.Layout)
}
func GetLocation() *time.Location {
	location, err := time.LoadLocation(commonConstant.ZoneTimeSH)
	if err != nil {
		//logger.LogErrorf(commonConstant.LoadTimeError, ", err : %+v", err)
		return time.Local
	}
	return location
}

func TimeIntervalCheck(params TimeParams) (startData, endData time.Time, err error) {
	if params.StartTime == "" || params.EndTime == "" {
		return time.Time{}, time.Time{}, errors.New("起止时间存在空数据")
	}

	startDecodeOld, err1 := TimeAddOps(&TimeFormatParams{TimeStr: params.StartTime, Location: GetLocation()})
	endDecode, err2 := TimeAddOps(&TimeFormatParams{TimeStr: params.EndTime, Location: GetLocation()})
	if err1 != nil || err2 != nil {
		err = TernaryExpression(err1 != nil, err1, err2)
		return time.Time{}, time.Time{}, errors.New(fmt.Sprintf("TimeIntervalCheck err, params: %+v, err: %+v", params, err))
	}

	if endDecode.Before(startDecodeOld) {
		return time.Time{}, time.Time{}, fmt.Errorf("开始时间晚于结束时间 (开始时间: %v, 结束时间: %v)", startDecodeOld.Format(commonConstant.Layout), endDecode.Format(commonConstant.Layout))
	}

	startDecode, err := TimeAddOps(&TimeFormatParams{
		TimeStr:  params.StartTime,
		TimeAdd:  &(params.TimeInterval),
		Location: GetLocation(),
	})
	if err != nil {
		return time.Time{}, time.Time{}, errors.New(fmt.Sprintf("TimeIntervalCheck err, params: %+v, err: %+v", params, err))
	}

	if endDecode.After(startDecode) && !params.Ops.NotAllowIntervalCheck {
		return time.Time{}, time.Time{}, fmt.Errorf("时间间隔超过最大值：%d天, (开始时间: %+v, 结束时间: %+v，按照开始时间计算允许的最大结束时间：%+v)",
			params.TimeInterval.Day, startDecodeOld.Format(commonConstant.Layout), endDecode.Format(commonConstant.Layout), startDecode.Format(commonConstant.Layout))
	}

	if params.Ops.NotAllowStartTimeBeforeNow {
		if startDecode.Before(TimeNow()) {
			return time.Time{}, time.Time{}, fmt.Errorf("开始时间早于当前时间(开始时间: %+v, 当前时间: %+v)", startDecode.Format(commonConstant.Layout), TimeNow().Format(commonConstant.Layout))
		}
	}

	if params.Ops.NotAllowEndTimeBeforeNow {
		if endDecode.Before(TimeNow()) {
			return time.Time{}, time.Time{}, fmt.Errorf("结束时间早于当前时间(结束时间: %+v, 当前时间: %+v)", endDecode.Format(commonConstant.Layout), TimeNow().Format(commonConstant.Layout))
		}
	}

	return startDecodeOld, endDecode, nil
}

func TimeSequenceConflict(timePairs ...*TimeParams) error {
	for _, pair := range timePairs {
		startTime, err := TimeDecode(&TimeFormatParams{TimeStr: pair.StartTime})
		if err != nil {
			return err
		}
		endTime, err := TimeDecode(&TimeFormatParams{TimeStr: pair.EndTime})
		if err != nil {
			return err
		}
		pair.StartTimeFormat = startTime.TimeFormat
		pair.EndTimeFormat = endTime.TimeFormat
	}

	if len(timePairs) <= 1 {
		return nil
	}

	// 按开始时间排序
	sort.Slice(timePairs, func(i, j int) bool {
		return timePairs[i].StartTimeFormat.Before(timePairs[j].StartTimeFormat)
	})

	for i := 0; i < len(timePairs)-1; i++ {
		if timePairs[i].EndTimeFormat.After(timePairs[i+1].StartTimeFormat) {
			return errors.New(fmt.Sprintf("存在时间冲突，【1】开始时间：%s，结束时间：%s，【2】开始时间：%s，开始时间：%s",
				timePairs[i].StartTimeFormat.Format(commonConstant.Layout),
				timePairs[i].EndTimeFormat.Format(commonConstant.Layout),
				timePairs[i+1].StartTimeFormat.Format(commonConstant.Layout),
				timePairs[i+1].EndTimeFormat.Format(commonConstant.Layout),
			))
		}
	}
	return nil
}

func TimeAddOps(timeParams *TimeFormatParams) (time.Time, error) {
	if timeParams == nil {
		return time.Now(), errors.New("params lost")
	}
	timeAdd := TernaryExpression(timeParams.TimeAdd == nil, &TimeInterval{}, timeParams.TimeAdd)
	timeFormat := timeParams.TimeFormat

	if timeParams.TimeStr != "" {
		timeDecode, err := TimeDecode(timeParams)
		if err != nil {
			return time.Now(), err
		}
		timeFormat = timeDecode.TimeFormat
	}

	if timeFormat.IsZero() {
		return time.Now(), errors.New("timeFormat IsZero")
	}

	timeFormat = timeFormat.AddDate(timeAdd.Year, timeAdd.Month, timeAdd.Day)

	return time.Date(
		timeFormat.Year(), timeFormat.Month(), timeFormat.Day(),
		TernaryExpression(timeParams.SupportBeforeDawn, timeAdd.Hour, timeFormat.Hour()+timeAdd.Hour),
		TernaryExpression(timeParams.SupportBeforeDawn, timeAdd.Min, timeFormat.Minute()+timeAdd.Min),
		TernaryExpression(timeParams.SupportBeforeDawn, timeAdd.Second, timeFormat.Second()+timeAdd.Second),
		0,
		TernaryExpression(timeParams.Location != nil, timeParams.Location, timeFormat.Location())), nil
}

func TimeDecode(timeInfo *TimeFormatParams) (*TimeFormatParams, error) {
	if timeInfo == nil || timeInfo.TimeStr == "" {
		return &TimeFormatParams{}, errors.New("timeInfo_nil")
	}

	timeStr := timeInfo.TimeStr
	formatStyle := TernaryExpression(timeInfo.FormatStyle == "", commonConstant.Layout, timeInfo.FormatStyle)
	decodeStyleArr := append([]string{timeInfo.DecodeStyle}, commonConstant.DecodeStyleArr...)

	for _, decodeStyle := range decodeStyleArr {
		if timeStandard, err := time.Parse(decodeStyle, timeStr); err == nil {
			return &TimeFormatParams{
				TimeStr:    timeStandard.Format(formatStyle),
				TimeFormat: timeStandard,
			}, nil
		}
	}

	return &TimeFormatParams{}, errors.New("Time_Decode_time_Parse_error")
}
